package com.sunday.common.core.utils;

import lombok.extern.slf4j.Slf4j;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

/**
 * ip工具类
 * <p>
 * 1字节(Byte)=8位(bit)
 * <p>
 * IPv4
 * 地址	长度为 32 位（4 个字节）。地址由网络和主机部分组成，这取决于地址类。根据地址的前几位，可定义各种地址类：A、B、C、D 或 E。IPv4 地址的总数为 4 294 967 296。
 * IPv4 地址的文本格式为 nnn.nnn.nnn.nnn，其中 0<=nnn<=255，而每个 n 都是十进制数。可省略前导零。最大打印字符数为 15 个，不计掩码。
 * <p>
 * IPv6
 * 地址	长度为 128 位（16 个字节）。基本体系结构的网络数字为 64 位，主机数字为 64 位。通常，IPv6 地址（或其部分）的主机部分将派生自 MAC 地址或其他接口标识。
 * 根据子网前缀，IPv6 的体系结构比 IPv4 的体系结构更复杂。
 * <p>
 * IPv6 地址的数目比 IPv4 地址的数目大 1028（79 228 162 514 264 337 593 543 950 336）倍。IPv6 地址的文本格式为 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx，其中每个 x 都是十六进制数，表示 4 位。
 * 可省略前导零。可在地址的文本格式中使用一次双冒号（::），用于指定任意数目的 0 位。例如，::ffff:10.120.78.40 表示 IPv4 映射的 IPv6 地址。
 * <p>
 * 十六进制（简写为hex或下标16）
 * 在数学中是一种逢16进1的进位制。一般用数字0到9和字母A到F（或a~f）表示，其中:A~F表示10~15，这些称作十六进制数字。
 *
 * @author zsy
 * @since 2022/10/11
 */
@Slf4j
public class IpUtils {

    private static final BigInteger position = new BigInteger("1");

    /**
     * 有符号位
     */
    public static final int IPV6_SIGNED = 17;
    public static final int IPV4_SIGNED = 5;
    /**
     * 无符号位
     */
    public static final int IPV6_UNSIGNED = 16;
    public static final int IPV4_UNSIGNED = 4;

    /**
     * 将字符串形式的ip地址转换为BigInteger
     *
     * @param addr
     * @return {@link BigInteger}
     */
    public static BigInteger ipToBigInteger(byte addr[]) {
        return new BigInteger(1, addr);
    }

    /**
     * 将整数偏移后获取ip地址转化为字符串
     *
     * @param ipInBigInt
     * @param offset
     * @return {@link String}
     */
    public static String bigIntegerOffsetToIp(BigInteger ipInBigInt, BigInteger offset) {
        return bigIntegerToIp(ipInBigInt.add(offset.subtract(position)));
    }

    /**
     * 将整数形式的ip地址转换为字符串形式
     *
     * @param ipInBigInt
     * @return {@link String}
     */
    public static String bigIntegerToIp(BigInteger ipInBigInt) {
        byte[] bytes = ipInBigInt.toByteArray();
        //查看是否存在符号位
        if (bytes.length == IPV4_SIGNED || bytes.length == IPV6_SIGNED) {
            bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
        }
        try {
            String ip = InetAddress.getByAddress(bytes).toString();
            return ip.substring(ip.indexOf('/') + 1).trim();
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

}
